package csel.controller.dispatchers;

import java.util.concurrent.LinkedBlockingQueue;
import csel.model.GameCommand;

/**
 * Reasons for Existence: To define the type of an AbstractDispatcher object</br>
 * Team Members Responsible</br>
 * Implementation: Nik, Tait</br>
 * Testing: Nik, Tait</br>
 * 
 * @author Nikolas Wolfe
 */
public abstract class AbstractDispatcher
{
	protected final DispatcherThread worker;
	protected final LinkedBlockingQueue<GameCommand> queue; 
	private Thread t;
	
	protected AbstractDispatcher()
	{
		this.queue = new LinkedBlockingQueue<GameCommand>();
		this.worker = new DispatcherThread();
		t = new Thread(worker);
	}
	
	// used by external command producer
	public abstract void export(GameCommand gc) throws InterruptedException;
	
	// used by worker thread
	public abstract void dispatchCommand() throws InterruptedException;
	
	public void startWorker()
	{
		t.start();
	}
	
	public void killWorker()
	{
		synchronized( worker )
		{
			t.interrupt();
			System.out.println("[CONTROLLER] Worker thread terminated!");
		}
	}
	
	public boolean isEmpty()
	{
		return queue.isEmpty();
	}
	
	// black box inheritance accessors
	protected DispatcherThread getWorker()
	{
		return worker;
	}
	
	protected LinkedBlockingQueue<GameCommand> getQueue()
	{
		return queue;
	}
	
	/**
	 * Reasons for Existence: Defines the behavior for dispatching command objects</br>
	 * Team Members Responsible</br>
	 * Implementation: Nik, Tait</br>
	 * Testing: Nik, Tait</br>
	 * 
	 * @author Nikolas Wolfe
	 */
	protected class DispatcherThread implements Runnable
	{
		public void run()
		{
			synchronized (this)
			{
				try
				{
					this.wait();
					System.out.println("[CONTROLLER] " + this.toString()
							+ " is awake!");
					while (!Thread.interrupted())
					{
						// if there's nothing in the queue, thread will wait
						if (!queue.isEmpty())
						{
							dispatchCommand();
						} else
						{
							this.wait();
						}
					}
				} catch (InterruptedException e)
				{
					Thread.currentThread().interrupt();
				}
			}
			System.out.println("The humanity!");
		}

		public String toString()
		{
			return this.getClass().getSimpleName();
		}
	}
	
	public String toString()
	{
		return this.getClass().getSimpleName();
	}
}
